package cn.ibizlab.util.command;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import cn.ibizlab.util.domain.IEntity;
import cn.ibizlab.util.helper.BeanCache;
import cn.ibizlab.util.helper.JacksonUtils;
import cn.ibizlab.util.helper.StringAdvUtils;
import cn.ibizlab.util.security.SpringContextHolder;
import com.alibaba.fastjson.util.TypeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ExecutionCmd {

    private ExecutionCmd() {
        throw new IllegalStateException("Utility class");
    }

    public static <R> ExecutionResult<R> execute(ExecutionCommandContext commandContext) {

        if(ObjectUtils.isEmpty(commandContext.getEntity())||ObjectUtils.isEmpty(commandContext.getAction()))
            return ExecutionResult.error(400,"缺失实体和行为参数");

        Object target = getEntityService(commandContext);
        if(target == null)
            return ExecutionResult.error(400,"未找到接口%1$sService", commandContext.getEntity());
        try {

            Class<?> entityClass = TypeUtil.getReturnClass(ReflectUtil.getMethod(target.getClass(),"getEntity"));
            if(commandContext.getOption("id")!=null && commandContext.getArgs()!=null) {
                BeanCache.FieldItem keyField = BeanCache.get(entityClass).getKeyField();
                commandContext.getArgs().values().forEach(arg->{
                    if(arg instanceof Map)
                        ((Map) arg).put(keyField.getCodeName().toLowerCase(), TypeUtils.cast(commandContext.getOption("id"),keyField.getField().getType(),null));
                });
            }

            String methodName = StringAdvUtils.camelcase(commandContext.getAction());
            List<Class<?>> clsTypes = new ArrayList<>();
            commandContext.getArgs().values().forEach(arg-> {
                if(arg!=null)
                    clsTypes.add(arg.getClass());
                else
                    clsTypes.add(Object.class);
            });
            Method method = null;
            Class[] classTypes = clsTypes.toArray(new Class[clsTypes.size()]);
            boolean isDEAction = !ObjectUtils.isEmpty(commandContext.getArgs().values().stream().filter(item -> item instanceof IEntity).collect(Collectors.toList()));
            Method[] methods = ReflectUtil.getMethods(target.getClass());
            if (!ObjectUtils.isEmpty(methods)) {
                //优先匹配相同参数、相同返回值的method
                List<Method> matchMethods = new ArrayList<>();
                for (Method m : methods) {
                    if(methodName.equals(m.getName())
                            && (ClassUtil.isAllAssignableFrom(m.getParameterTypes(), classTypes) || (isDEAction && ClassUtil.isAllAssignableFrom(classTypes,m.getParameterTypes())))
                            && (method == null|| method.getReturnType().isAssignableFrom(method.getReturnType()))){
                            method = m;
                            matchMethods.add(m);
                    }
                }
                //查找到多个method时，优先匹配相同类型，如：save(entity)、save(T)
                if (!ObjectUtils.isEmpty(matchMethods)) {
                    method = matchMethods.size() > 1 && isDEAction ? getMethod(matchMethods) : matchMethods.get(0);
                }
                //未找到相同参数method时，通过方法名进行查找
                if (method == null) {
                    for (Method m : methods) {
                        if (methodName.equalsIgnoreCase(m.getName())&&TypeUtil.getParamClasses(m).length == commandContext.getArgs().size())
                            method = m;
                    }
                }
            }
            if(method==null)
                return ExecutionResult.error(400,"未找到接口%1$sService的%2$s方法", commandContext.getEntity(),commandContext.getAction());

            List args = new ArrayList();
            Class[] types = TypeUtil.getParamClasses(method);
            if (types.length != commandContext.getArgs().size())
                return ExecutionResult.error(400, "未找到接口%1$sService的%2$s方法，参数个数不对", commandContext.getEntity(), commandContext.getAction());
            if (types != null) {
                for (int i = 0; i < types.length; i++) {
                    Class cls = types[i];
                    Object obj = commandContext.getArg(i);
                    if (obj != null && !obj.getClass().isAssignableFrom(cls)) {
                        //批操作参数转换：如：createBatch、updateBatch等，List<EntityBase> -> List<Entity>
                        if (!ObjectUtils.isEmpty(obj)&& obj instanceof List && List.class.isAssignableFrom(cls) && ((List<?>) obj).get(0) != null && ((List<?>) obj).get(0) instanceof IEntity) {
                            args.add(JacksonUtils.toArray(JacksonUtils.toJson(obj), entityClass));
                        } else if (cls.isArray()) { //数组类型参数转换
                            args.add(JacksonUtils.toArray(JacksonUtils.toJson(obj), TypeUtil.getClass(TypeUtil.getParamTypes(method)[i])));
                        } else { //对象类型数据转换，如：entityBase、map -> entity
                            args.add(JacksonUtils.toObj(JacksonUtils.toJson(obj), cls));
                        }
                    } else if (obj instanceof IEntity && obj.getClass().isAssignableFrom(cls)) {
                        args.add(JacksonUtils.toObj(JacksonUtils.toJson(obj), cls));
                    } else
                        args.add(obj);
                }
            }

            R ret = ReflectUtil.invoke(target, method, args.toArray());
            return ExecutionResult.ok(ret);
        }catch (Exception ex) {
            log.error("调用{}.{}失败，{}，{}",commandContext.getEntity(),commandContext.getAction(),JacksonUtils.toJson(commandContext),ex);
            return ExecutionResult.error("调用%1$s.%2$s失败，%3$s",commandContext.getEntity(),commandContext.getAction(),ex.getMessage());
        }
    }

    /**
     * 查找方法参数类型为IEntity
     * @param matchMethods
     * @return
     */
    private static Method getMethod(List<Method> matchMethods){
        List<Class<?>> clsTypes = new ArrayList<>();
        clsTypes.add(IEntity.class);
        Class[] classTypes = clsTypes.toArray(new Class[clsTypes.size()]);
        List<Method> methods = matchMethods.stream().filter(item -> ClassUtil.isAllAssignableFrom(classTypes, item.getParameterTypes())).collect(Collectors.toList());
        Method method = !ObjectUtils.isEmpty(methods)? methods.get(0) : matchMethods.get(0);
        return method;
    }

    /**
     * 获取实体服务对象
     * @param commandContext
     * @return
     */
    protected static Object getEntityService(ExecutionCommandContext commandContext){
        Object target = null;
        try {
            target = SpringContextHolder.getBean(StringAdvUtils.pascalcase(commandContext.getEntity())+"Service");
        } catch (Exception e) {
            target = SpringContextHolder.getBean(commandContext.getEntity()+"Service");
        }
        return  target;
    }
}
